/*  This program is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation; either version 2.1 of the License, or (at
    your option) any later version.
    For more details, see the GNU Lesser General Public License (www.fsf.org
    or the COPYING file somewhere in the package)
 */

/*! @file test/Services.cc
    @brief Class Services (non-inline) methods.
    @author @ref Guillaume_Terrissol
    @date 21st February 2010 - 29th January 2014
    @note This file is distributed under the LGPL license.
    Refer to the file COPYING (or http://www.fsf.org) for more information.
 */

#include <cppunit/extensions/HelperMacros.h>

#include <BundleInstallerService.hh>
#include <BundleMgr.hh>
#include <Exception.hh>
#include <ExtensionPointService.hh>

#include "Utils.hh"

namespace CHK
{
//------------------------------------------------------------------------------
//                                  Test Class
//------------------------------------------------------------------------------

    /*! @brief Standard checks for services.
        @version 0.9
     */
    class Services : public CppUnit::TestFixture
    {
#ifndef NOT_FOR_DOXYGEN
        // Tests registering
        CPPUNIT_TEST_SUITE(Services);
        CPPUNIT_TEST(retrievingServices);
        CPPUNIT_TEST(extensionPoints);
        CPPUNIT_TEST(installBundleNominal);
        CPPUNIT_TEST(installBundleWithDependencies);
        CPPUNIT_TEST(installBundleWithMissingDependencies);
        CPPUNIT_TEST(replaceBundleNominal);
        CPPUNIT_TEST(replaceBundleFailed);
        CPPUNIT_TEST_EXCEPTION(reinstallBundleFailed, OSGi::BundleError);
        CPPUNIT_TEST_SUITE_END();
#endif  // NOT_FOR_DOXYGEN
    public:
        //! @name "Interface"
        //@{
virtual         ~Services() = default;                  //!< Destructor.
virtual void    setUp() override;                       //!< Preparation.
        //@}
    private:
        //! @name Testing
        //@{
        void    retrievingServices();                   //!< ... basic service management.
        void    extensionPoints();                      //!< ... extension points service.
        void    installBundleNominal();                 //!< ... bundle manual installation.
        void    installBundleWithDependencies();        //!< ... bundle manual installation (with dependency).
        void    installBundleWithMissingDependencies(); //!< ... bundle manual installation (with missing dependency).
        void    replaceBundleNominal();                 //!< ... bundle nominal replacement.
        void    replaceBundleFailed();                  //!< ... bundle failed replacement.
        void    reinstallBundleFailed();                //!< ... bundle failed reinstallation.
        //@}
    };

    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(Services, "Checking services");


//------------------------------------------------------------------------------
//                                  "Interface"
//------------------------------------------------------------------------------

    /*! Cleanup between tests.
     */
    void Services::setUp()
    {
        removeBundles();
    }


//------------------------------------------------------------------------------
//                                     Tests
//------------------------------------------------------------------------------

    /*! @test @par Checking basic service management.
        Checks the presence of default services.@n
        Checks in and out services, and tests their validity.
     */
    void Services::retrievingServices()
    {
        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });

        auto    lBI = lMgr->services()->findByName("osgi.core.installer");
        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lBI);
        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is invalid",
                               std::dynamic_pointer_cast<OSGi::BundleInstallerService>(lBI));

        auto    lXP = lMgr->services()->findByName("osgi.core.xp");
        CPPUNIT_ASSERT_MESSAGE("Extension point service is missing",
                               lXP);
        CPPUNIT_ASSERT_MESSAGE("Extension point service is invalid",
                               std::dynamic_pointer_cast<OSGi::ExtensionPointService>(lXP));

        lMgr->services()->checkOut("osgi.core.xp");
        CPPUNIT_ASSERT_MESSAGE("Service shouldn't be here any more",
                               !lMgr->services()->findByName("osgi.core.xp"));

        try
        {
            lMgr->services()->checkIn("osgi.core.installer", lXP);
        }
        catch(OSGi::LogicError& pE)
        {
            CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                    std::string(pE.what()) == "Already existing service : osgi.core.installer");

        }

        lMgr->services()->checkIn("osgi.core.xp", lXP);
        lMgr->services()->checkIn("osgi.core.xp2", lXP);
        CPPUNIT_ASSERT_MESSAGE("Extension point service is missing",
                               lMgr->services()->findByName("osgi.core.xp"));
        CPPUNIT_ASSERT_MESSAGE("Extension point service (2) is missing",
                               lMgr->services()->findByName("osgi.core.xp2"));
        lMgr->services()->checkOut(lXP);
        CPPUNIT_ASSERT_MESSAGE("Extension point service is missing again",
                               bool(lMgr->services()->findByName("osgi.core.xp")) ^
                               bool(lMgr->services()->findByName("osgi.core.xp2")));
        lMgr->services()->checkOut(lXP);
        CPPUNIT_ASSERT_MESSAGE("Extension point service is still there",
                               !lMgr->services()->findByName("osgi.core.xp") &&
                               !lMgr->services()->findByName("osgi.core.xp2"));

        CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str().empty());
        CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
    }


    /*! @test @par Checking extension points.
        Starts all test bundles with extension points. Expected extensions are checked.@n
        Starts a bundle with an extension point, which is manually removed. A bundle with a matching extension is
        started : the extension isn't loaded.
     */
    void Services::extensionPoints()
    {
        {
            RedirectStream  lHideOutput{"stdout.txt", stdout};
            RedirectStream  lHideError{"stderr.txt", stderr};

            removeBundles();
            installBundle("fr.osgi.test.xp.root1_1.0.0.bndl");
            installBundle("fr.osgi.test.xp.root2_1.0.0.bndl");
            installBundle("fr.osgi.test.xp.ext1_1.0.0.bndl");
            installBundle("fr.osgi.test.xp.ext2_1.0.0.bndl");
            installBundle("fr.osgi.test.xp.ext3_1.0.0.bndl");

            auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
            lMgr->loadBundles();
            lMgr->startBundles();

            lMgr->finalize();
            removeBundles();

            CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                                   "Bundle bundles/fr.osgi.test.xp.ext1_1.0.0.bndl installed\n"
                                   "Bundle bundles/fr.osgi.test.xp.ext2_1.0.0.bndl installed\n"
                                   "Bundle bundles/fr.osgi.test.xp.ext3_1.0.0.bndl installed\n"
                                   "Bundle bundles/fr.osgi.test.xp.root1_1.0.0.bndl installed\n"
                                   "Bundle bundles/fr.osgi.test.xp.root2_1.0.0.bndl installed\n"
                                   "Bundle fr.osgi.test.xp.ext1 resolved\n"
                                   "Bundle fr.osgi.test.xp.ext2 resolved\n"
                                   "Bundle fr.osgi.test.xp.ext3 resolved\n"
                                   "Starting XPROOT1::Activator\n"
                                   "Starting XPEXT1::Activator\n"
                                   "Starting XPROOT2::Activator\n"
                                   "Starting XPEXT2::Activator\n"
                                   "Starting XPEXT3::Activator\n"
                                   "Stopping XPEXT3::Activator\n"
                                   "Stopping XPEXT2::Activator\n"
                                   "Stopping XPROOT2::Activator\n"
                                   "Stopping XPEXT1::Activator\n"
                                   "Stopping XPROOT1::Activator\n"
                                   );

            CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
        }

        CPPUNIT_ASSERT_MESSAGE("Output log as cout failed",
                               dynamic_cast<std::ostringstream&>(std::ostringstream{}
                                                                        << std::fstream{"stdout.txt",
                                                                                        std::fstream::in | std::fstream::binary}.rdbuf()
                                                                 ).str() ==
                               "XP::doHandleExtension(fr.osgi.test.xp.ext1, <extension point=\"xp_1st\"><comment>This is an extension point</comment></extension>)\n"
                               "XP::doHandleExtension(fr.osgi.test.xp.ext2, <extension point=\"xp_2nd\"><comment>This is another extension point</comment></extension>)\n"
                               "XP::doRemoveExtension(fr.osgi.test.xp.ext2, <extension point=\"xp_2nd\"><comment>This is another extension point</comment></extension>)\n"
                               "XP::doRemoveExtension(fr.osgi.test.xp.ext1, <extension point=\"xp_1st\"><comment>This is an extension point</comment></extension>)\n");
        CPPUNIT_ASSERT_MESSAGE("Error log as cerr failed",
                               dynamic_cast<std::ostringstream&>(std::ostringstream{}
                                                                        << std::fstream{"stderr.txt",
                                                                                        std::fstream::in | std::fstream::binary}.rdbuf()
                                                                ).str().empty());
        {
            RedirectStream  lHideOutput{"stdout.txt", stdout};
            RedirectStream  lHideError{"stderr.txt", stderr};

            removeBundles();
            installBundle("fr.osgi.test.xp.root1_1.0.0.bndl");

            auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
            lMgr->loadBundles();
            lMgr->startBundles();

            auto    lXPService  = lMgr->services()->findByTypeAndName<OSGi::ExtensionPointService>("osgi.core.xp");
            CPPUNIT_ASSERT_MESSAGE("Extension point service is missing",
                                   lXPService);
            lXPService->checkOut("xp_1st");

            auto    lInstaller  = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");
            CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                                   lInstaller);

            auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";
            auto    lExt1 = lInstaller->installBundle(lPath + "fr.osgi.test.xp.ext1_1.0.0.bndl");
            lExt1->act(OSGi::kResolve, lMgr->context());
            lExt1->act(OSGi::kStart,   lMgr->context());

            lMgr->finalize();
            removeBundles();

            CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                                   "Bundle bundles/fr.osgi.test.xp.root1_1.0.0.bndl installed\n"
                                   "Bundle fr.osgi.test.xp.root1 resolved\n"
                                   "Starting XPROOT1::Activator\n"
                                   "Starting XPEXT1::Activator\n"
                                   "Stopping XPEXT1::Activator\n"
                                   "Stopping XPROOT1::Activator\n");
            CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
        }

        CPPUNIT_ASSERT_MESSAGE("Output log as cout failed",
                               dynamic_cast<std::ostringstream&>(std::ostringstream{}
                                                                        << std::fstream{"stdout.txt",
                                                                                        std::fstream::in | std::fstream::binary}.rdbuf()
                                                                 ).str().empty());
        CPPUNIT_ASSERT_MESSAGE("Error log as cerr failed",
                               dynamic_cast<std::ostringstream&>(std::ostringstream{}
                                                                        << std::fstream{"stderr.txt",
                                                                                        std::fstream::in | std::fstream::binary}.rdbuf()
                                                                ).str().empty());
    }


    /*! @test @par Checking bundle manual installation.
        A bundle is installed with the service @ref OSGi::BundleInstallerService "osgi.core.installer", and its life
        cycle is checked step by step.
     */
    void Services::installBundleNominal()
    {
        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
        lMgr->loadBundles();
        lMgr->startBundles();

        auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.root");
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.root shoudn't be there",
                               !lBndl);

        auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");

        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lInstaller);

        auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";
        auto    lRoot = lInstaller->installBundle(lPath + "fr.osgi.test.root_1.0.0.bndl");
        CPPUNIT_ASSERT_MESSAGE("Bundles mismatch",
                               lRoot == lMgr->context()->findBundle("fr.osgi.test.root"));
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.root is missing",
                               lRoot);
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root isn't installed",
                               lRoot->state() == "installed");
        lRoot->act(OSGi::kResolve, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root hasn't been resolved",
                               lRoot->state() == "resolved");

        lRoot->act(OSGi::kStart, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root hasn't been started",
                               lRoot->state() == "active");

        lRoot->act(OSGi::kStop, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root hasn't been stopped",
                               lRoot->state() == "resolved");

        lMgr->finalize();
        removeBundles();

        CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                               "Starting ROOT::Activator\n"
                               "Stopping ROOT::Activator\n");
        CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
    }


    /*! @test @par Checking manual installation of a bundle with a dependency.
        A bundle and its dependency are manually installed; their resolution succeeds.
     */
    void Services::installBundleWithDependencies()
    {
        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
        lMgr->loadBundles();
        lMgr->startBundles();

        auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.root");
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.root shoudn't be there",
                               !lBndl);

        auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");

        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lInstaller);

        auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";
        auto    lNeedRoot = lInstaller->installBundle(lPath + "fr.osgi.test.needroot_1.0.0.bndl");
        CPPUNIT_ASSERT_MESSAGE("Bundles mismatch",
                               lNeedRoot == lMgr->context()->findBundle("fr.osgi.test.needroot"));
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.needroot is missing",
                               lNeedRoot);
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.needroot isn't installed",
                               lNeedRoot->state() == "installed");

        auto    lRoot = lInstaller->installBundle(lPath + "fr.osgi.test.root_1.0.0.bndl");
        CPPUNIT_ASSERT_MESSAGE("Bundles mismatch",
                               lRoot == lMgr->context()->findBundle("fr.osgi.test.root"));
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.root is missing",
                               lRoot);
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root isn't installed",
                               lRoot->state() == "installed");
        lRoot->act(OSGi::kResolve, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root hasn't been resolved",
                               lRoot->state() == "resolved");

        lNeedRoot->act(OSGi::kResolve, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.needroot hasn't been resolved",
                               lNeedRoot->state() == "resolved");

        lNeedRoot->act(OSGi::kStart, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.root hasn't been started",
                               (lRoot->state() == OSGi::kActive));
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.needroot hasn't been started",
                               (lNeedRoot->state() == OSGi::kActive));

        lMgr->finalize();
        removeBundles();

        CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                               "Starting ROOT::Activator\n"
                               "Starting NEEDROOT::Activator\n"
                               "Stopping NEEDROOT::Activator\n"
                               "Stopping ROOT::Activator\n");
        CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
    }


    /*! @test @par Checking manual installation of a bundle with a missing dependency.
        A bundle with a missing dependency is manually installed; its resolution fails.
     */
    void Services::installBundleWithMissingDependencies()
    {
        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
        lMgr->loadBundles();
        lMgr->startBundles();

        auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.missingdep");
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.missingdep shoudn't be there",
                               !lBndl);

        auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");
        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lInstaller);

        auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";
        auto    lMiss = lInstaller->installBundle(lPath + "fr.osgi.test.missdep_1.0.0.bndl");
        CPPUNIT_ASSERT_MESSAGE("Bundles mismatch",
                               lMiss == lMgr->context()->findBundle("fr.osgi.test.missdep"));
        CPPUNIT_ASSERT_MESSAGE("Bundle fr.osgi.test.missingdep is missing",
                               lMiss);
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.missingdep isn't installed",
                               lMiss->state() == "installed");
        lMiss->act(OSGi::kResolve, lMgr->context());
        CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.missingdep has been unexpectedly resolved",
                               lMiss->state() == "installed");

        lMgr->finalize();
        removeBundles();

        CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str().empty());
        CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str() ==
                               "Missing dependency : fr.osgi.test.r00t 0.0.0\n");
    }


    /*! @test @par Checking nominal bundle replacement.
        Automatically installs a bundle. Checks that it can't be replaced while still active.@n
        Stops it, and replaces it with a higher version. Checks the replacement succeeded.
     */
    void Services::replaceBundleNominal()
    {
        installBundle("fr.osgi.test.alone_1.2.4.bndl");

        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
        lMgr->loadBundles();
        lMgr->startBundles();

        auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.alone");
        CPPUNIT_ASSERT_MESSAGE("Missing bundle : fr.osgi.test.alone",
                               lBndl);

        auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");
        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lInstaller);

        auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";

        try
        {
            lInstaller->replaceBundle("fr.osgi.test.alone", lPath + "fr.osgi.test.alone_1.3.2.bndl");
        }
        catch(OSGi::BundleError& pE)
        {
            CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                    std::string(pE.what()) == "Bundle fr.osgi.test.alone couldn't be uninstalled");
        }

        lBndl->act(OSGi::kStop, lMgr->context());
        auto    lNew = lInstaller->replaceBundle("fr.osgi.test.alone", lPath + "fr.osgi.test.alone_1.3.2.bndl");

        CPPUNIT_ASSERT_MESSAGE("Replace bundle failed", lNew);
        CPPUNIT_ASSERT_MESSAGE("Wrong version", lNew->version() == "1.3.2");
        CPPUNIT_ASSERT_MESSAGE("Wrong name", lNew->name() == "fr.osgi.test.alone");
        CPPUNIT_ASSERT_MESSAGE("Wrong pointer", lNew == lBndl);
        CPPUNIT_ASSERT_MESSAGE("Wrong state", lNew->state() == OSGi::kResolved);

        lNew->act(OSGi::kStart, lMgr->context());

        lMgr->finalize();
        removeBundles();

        CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                               "Bundle bundles/fr.osgi.test.alone_1.2.4.bndl installed\n"
                               "Bundle fr.osgi.test.alone resolved\n"
                               "Starting ALONE::Activator\n"
                               "Stopping ALONE::Activator\n"
                               "Starting ALONE::Activator\n"
                               "Stopping ALONE::Activator\n");
        CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                               dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
    }


    /*! @test @par Checking nominal bundle replacement.
        Checks that a missing (or misspelled) bundle can't be replaced.@n
        Checks that a replacer bundle must exists.
     */
    void Services::replaceBundleFailed()
    {
        {
            removeBundles();

            auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
            lMgr->loadBundles();
            lMgr->startBundles();

            CPPUNIT_ASSERT_MESSAGE("Unexpected bundles",
                                   lMgr->context()->bundles().size() == 0);

            auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");
            CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                                   lInstaller);

            auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";

            try
            {
                lInstaller->replaceBundle("fr.osgi.test.alone", lPath + "fr.osgi.test.alone_1.3.2.bndl");
            }
            catch(OSGi::BundleError& pE)
            {
                CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                        std::string(pE.what()) == "Can't replace missing bundle fr.osgi.test.alone");
            }

            lInstaller->installBundle(lPath + "fr.osgi.test.alone_1.0.0.bndl");
            auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.alone");
            CPPUNIT_ASSERT_MESSAGE("Missing bundle : fr.osgi.test.alone",
                                   lBndl);

            try
            {
                lInstaller->replaceBundle("fr.osgi.test.Alone", lPath + "fr.osgi.test.alone_1.3.2.bndl");
            }
            catch(OSGi::BundleError& pE)
            {
                CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                        std::string(pE.what()) == "Can't replace missing bundle fr.osgi.test.Alone");
            }

            lBndl->act(OSGi::kResolve, lMgr->context());
            lBndl->act(OSGi::kStart, lMgr->context());
            CPPUNIT_ASSERT_MESSAGE("fr.osgi.test.alone hasn't been started",
                                   lBndl->state() == OSGi::kActive);

            try
            {
                lInstaller->replaceBundle("fr.osgi.test.alone", lPath + "fr.osgi.test.alone_1.3.2.bndl");
            }
            catch(OSGi::BundleError& pE)
            {
                CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                        std::string(pE.what()) == "Bundle fr.osgi.test.alone couldn't be uninstalled");
            }

            lBndl->act(OSGi::kStop, lMgr->context());
            try
            {
                lInstaller->replaceBundle("fr.osgi.test.alone", lPath + "fr.osgi.test.alone_1.2.3.bndl");
            }
            catch(OSGi::IOError& pE)
            {
                CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                        std::string(pE.what()) == "Failed to open ../bundles/fr.osgi.test.alone_1.2.3.bndl");
            }
// + resolve failure. + install bundle failed.
// + attempt to uninstall a bundle the dependees of which are still there...
// + review all weird cases, and check them.
            lMgr->finalize();
            removeBundles();

            CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                                   "Starting ALONE::Activator\n"
                                   "Stopping ALONE::Activator\n");
            CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());
        }
    }


    /*! @test @par Checking failed reinstallation.
        Automatically installs a bundle.@n
        Manual installation of a higher version fails.
     */
    void Services::reinstallBundleFailed()
    {
        installBundle("fr.osgi.test.alone_1.2.4.bndl");

        auto    lMgr = OSGi::BundleMgr::make({ "cleanCache", "verbose", "out=string", "err=string" });
        lMgr->loadBundles();
        lMgr->startBundles();

        auto    lBndl   = lMgr->context()->findBundle("fr.osgi.test.alone");
        CPPUNIT_ASSERT_MESSAGE("Missing bundle : fr.osgi.test.alone",
                               lBndl);

        auto    lInstaller = lMgr->services()->findByTypeAndName<OSGi::BundleInstallerService>("osgi.core.installer");
        CPPUNIT_ASSERT_MESSAGE("Bundle installer service is missing",
                               lInstaller);

        auto    lPath = lMgr->properties()->get("application.dir") + "/../bundles/";

        try
        {
            lInstaller->installBundle(lPath + "fr.osgi.test.alone_1.3.2.bndl");
        }
        catch(OSGi::BundleError& pE)
        {
            CPPUNIT_ASSERT_MESSAGE("Unexpected exception",
                                    std::string(pE.what()) == "Bundle fr.osgi.test.alone already defined with different Id");

            lMgr->finalize();
            removeBundles();

            CPPUNIT_ASSERT_MESSAGE("Unexpected message on output log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->out()).str() ==
                                   "Bundle bundles/fr.osgi.test.alone_1.2.4.bndl installed\n"
                                   "Bundle fr.osgi.test.alone resolved\n"
                                   "Starting ALONE::Activator\n"
                                   "Stopping ALONE::Activator\n");
            CPPUNIT_ASSERT_MESSAGE("Unexpected message on error log",
                                   dynamic_cast<std::ostringstream&>(lMgr->context()->err()).str().empty());

            throw;
        }

        throw std::exception();
    }
}
